﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using RayDen.Library.Components.Surface;
using RayDen.Library.Components.SystemComponents;
using RayDen.Library.Core.Primitives;
using RayDen.Library.Data;
using RayDen.Library.Entity.Interface;
using RayDen.Library.Entity.Scene;
using RayDen.RayEngine.Core.Surface;

namespace RayDen.RayEngine.Scene
{
    public class RayEngineMaterialBuilder
    {
        private Dictionary<string, RayEngineMaterial> materialMap;
        private RayEngineScene scene;
        private string[] names;
        public void BuildMaterials(RayEngineScene scene)
        {
            this.scene = scene;
            this.BuildMap(scene.MaterialProvider);
            foreach (var triangleMesh in scene.Meshes)
            {
                triangleMesh.Material = materialMap[triangleMesh.MaterialName];
            }
        }

        private RgbSpectrum GetValue(TextureType t, MaterialInfo mi)
        {
            switch (t)
            {
                case TextureType.Alpha:
                    return new RgbSpectrum();
                case TextureType.Bump:
                    return new RgbSpectrum();
                case TextureType.Normal:
                    return new RgbSpectrum();
                case TextureType.Gloss:
                    return mi.Ks;
                case TextureType.Reflection:
                    return mi.Kr;
                case TextureType.Refraction:
                    return mi.Kt;
                case TextureType.Diffuse:
                default:
                    return mi.Kd;
            }
        }

        private void BuildMap(IMaterialProvider materialProvider)
        {
            materialMap = new Dictionary<string, RayEngineMaterial>(StringComparer.InvariantCultureIgnoreCase);
            var texSampler = scene.TextureSamplingQuality == TextureSamplingQuality.Linear
                ? (ITextureSampler)new LinearTextureSampler()
                : new NearestTextureSampler();
            Func<TextureType, MaterialInfo, ITexture> textureResolver = (id, mi) =>
            {

                var tex = scene.Query(scene.MatLib.GetID(mi.MaterialName.ToLower()), id);
                var texVal = this.GetValue(id, mi);
                if (tex == null )
                {
                    if (!texVal.IsBlack())
                        tex = new ConstSpectrumTexture((RgbSpectrumInfo) this.GetValue(id, mi), 0, 0);
                    else
                    {
#if VERBOSE
                        Tracer.TraceLine("Material loading warning - {0} material {1} texture is black {2}", mi.MaterialName, id, texVal);
#endif
                    }
                }
                return tex;
            };
            foreach (var materialInfo in materialProvider.GetAll())
            {
                RayEngineMaterial material = null;

                switch (BrdfClassNameMapper.GetMaterialClass(materialInfo))
                {

                    case BrdfClass.GlossyMetal:
                        material = new GenericRayEngineMaterial<MetalBrdf>(materialInfo, texSampler, textureResolver);
                        break;

                    case BrdfClass.Glass:
                        materialInfo.MediumInfo = MediumInfo.Glass;
                        material = new GenericRayEngineMaterial<GlassBrdf>(materialInfo, texSampler, textureResolver);
                        break;

                    case BrdfClass.Mirror:
                        material = new GenericRayEngineMaterial<SpecularReflectionBrdf>(materialInfo, texSampler, textureResolver);
                        break;

                    case BrdfClass.DiffuseLambert:
                        material = new GenericRayEngineMaterial<MatteLambertBrdf>(materialInfo, texSampler, textureResolver);
                        break;

                    default:
                        material = new GenericRayEngineMaterial<MatteLambertBrdf>(materialInfo, texSampler, textureResolver);
                        break;

                }

                if (material != null)
                {
                    materialMap.Add(material.Name, material);
                }
            }


        }


    }
}
